Skip to content

Optimize FP16 LayerNorm and RMSNorm on x86 - #32715

Merged
mirounga merged 3 commits into
mainfrom
perf/cpu-half-layer-norm
Sep 25, 2026
Merged

mirounga merged 3 commits into
mainfrom
perf/cpu-half-layer-norm

Conversation

@xadupre

@xadupre Xavier Dupré (xadupre) commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

Description

Add an AVX2/FMA/F16C MLAS kernel for FP16 LayerNorm and RMSNorm on x86. The kernel widens FP16 inputs in vectors, performs the reductions and affine transform in FP32 (with a double-precision mean accumulation for LayerNorm), and narrows the result back to FP16 in vectors.

The CPU LayerNorm implementation dispatches to this kernel for rows of at least 16 elements when the processor supports the required instructions. Other architectures, older x86 processors, short rows, BF16, and unsupported output-statistics types retain the existing scalar path.

Motivation and Context

The existing FP16 CPU path converts every input and output element individually around scalar normalization math. Transformer workloads commonly execute LayerNorm and RMSNorm over rows of 1K-4K elements, where conversion and scalar arithmetic dominate execution time.

The benchmark processes 4,194,304 FP16 elements per invocation with rows parallelized across OpenMP workers, mirroring ORT's per-row thread-pool parallelism. It was pinned to one 48-core Intel Xeon Platinum 8480C socket with workers bound to cores. The table reports the median speedup from three process runs; graph/session overhead is intentionally excluded.

Operation Row size 1 thread 8 threads 16 threads 32 threads
LayerNorm 1024 15.66x 15.39x 15.85x 14.89x
RMSNorm 1024 12.07x 11.91x 11.96x 10.63x
LayerNorm 4096 14.96x 15.72x 14.41x 13.31x
RMSNorm 4096 11.71x 11.42x 10.84x 9.61x

At 32 threads, the optimized kernels reach median throughputs of 54.0-57.6 billion elements/s for LayerNorm and 81.2-89.4 billion elements/s for RMSNorm.

Validation:

  • Built onnxruntime_mlas_test and onnxruntime_provider_test in a CPU Release configuration.
  • MlasLayerNormF16Test.MatchesFloatReference passes for LayerNorm with/without bias and RMSNorm at row sizes 16, 31, and 128.
  • RMSNormalizationOpTest.RMSNorm_float16_OptimizedRow passes through the CPU execution provider.
  • Existing RMSNormalizationOpTest.* suite passes (26 tests before adding the dedicated optimized-row case).
  • Lintrunner passes on all changed files.

Add an AVX2/F16C MLAS kernel for FP16 LayerNorm and RMSNorm and dispatch it from the CPU implementation while retaining the scalar fallback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings September 21, 2026 16:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The kernel breaks 32-bit Unix builds without -mf16c and returns an incorrect simplified MeanOut value.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity · 1 Low severity

Open (3)
What changed in this PR

Adds an AVX2/FMA/F16C FP16 LayerNorm/RMSNorm kernel for x86 and dispatches eligible CPU workloads to it.

Changes:

  • Implements vectorized FP16 normalization with FP32 reductions.
  • Adds CPU feature dispatch and MLAS API plumbing.
  • Adds MLAS and CPU-provider tests.
File Description
onnxruntime/​test/​providers/​cpu/​nn/​rms_norm_op_test.cc Tests an optimized FP16 RMSNorm row.
onnxruntime/​test/​mlas/​unittest/​test_layernorm.cpp Tests FP16 MLAS normalization.
onnxruntime/​core/​providers/​cpu/​nn/​layer_norm_impl.cc Dispatches eligible FP16 rows to MLAS.
onnxruntime/​core/​mlas/​lib/​platform.cpp Registers the kernel on supported x86 CPUs.
onnxruntime/​core/​mlas/​lib/​mlasi.h Defines internal kernel types and dispatch state.
onnxruntime/​core/​mlas/​lib/​layernorm.cpp Adds the FP16 dispatch wrapper.
onnxruntime/​core/​mlas/​lib/​layernorm_kernel_avx2.cpp Implements the AVX2 FP16 kernel.
onnxruntime/​core/​mlas/​inc/​mlas.h Exposes the FP16 MLAS API.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread onnxruntime/core/mlas/lib/layernorm_kernel_avx2.cpp Outdated
Comment thread onnxruntime/core/mlas/lib/layernorm_kernel_avx2.cpp
Comment thread onnxruntime/core/providers/cpu/nn/layer_norm_impl.cc
Enable F16C when compiling the 32-bit x86 kernel, return the RMSNorm mean when requested, and cover optimized LayerNorm provider wiring end to end.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the supported round-to-nearest immediate for F16C conversions. MSVC rejects _MM_FROUND_NO_EXC with C4556 because only bits 0-2 are valid; the extra bit is ignored by VCVTPS2PH and does not suppress exceptions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@xadupre

Copy link
Copy Markdown
Member Author

Fixed the Windows MSVC build failure in ba7dfc0. The three FP16 output conversions passed _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC (8) to _mm256_cvtps_ph, but MSVC only accepts immediates 0-7 and reports C4556, promoted to C2220 by warnings-as-errors. All three paths now use _MM_FROUND_TO_NEAREST_INT, matching the existing MLAS F16C kernels and preserving rounding behavior.

The three existing review threads are already addressed and resolved. The push starts fresh CI for this fix; old failed runs were not rerun.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The implementation, platform gating, fallback behavior, and targeted tests are consistent and the prior issues are resolved.

Review effort: Balanced
Findings: None

Resolved since last review (3)

@mirounga
mirounga merged commit f403a9d into main Sep 25, 2026
91 checks passed
@mirounga
mirounga deleted the perf/cpu-half-layer-norm branch September 25, 2026 16:49
This was referenced Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants